#include <fcntl.h>
#include <linux/spi/spidev.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <unistd.h>

#include "platforms/spi.h"
#include "retval.h"

typedef struct {
	spi_conf_t conf;
	char dev_name[32];
	int fd;
	bool inited;
} spi_ctxt_t;

spi_t spi_init(spi_conf_t conf)
{
	int rc = 0;              /* return code */
	int fd = 0;              /* file descriptor */
	int mode = 0;            /* spi mode */
	int speed = 0;           /* spi speed */
	spi_ctxt_t* ctxt = NULL; /* spi context */

	/* 参数有效性校验 */
	switch (conf.mode) {
	case SPI_E_MODE_0:
		mode = SPI_MODE_0;
		break;
	case SPI_E_MODE_1:
		mode = SPI_MODE_1;
		break;
	case SPI_E_MODE_2:
		mode = SPI_MODE_2;
		break;
	case SPI_E_MODE_3:
		mode = SPI_MODE_3;
		break;
	default:
		printf("inval: conf.mode = %d\n", conf.mode);
		return NULL;
	}

	/* 打开 SPI 设备节点 */
	fd = open("/dev/spidev1.0", O_RDWR);
	if (fd < 0) {
		printf("err: open(%s, O_RDWR), ret = %d\n", "/dev/spidev1.0", fd);
		return NULL;
	}

	/* 设置 SPI 模式 */
	rc = ioctl(fd, SPI_IOC_WR_MODE, &mode);
	if (rc) {
		printf("err: ioctl(%d, SPI_IOC_WR_MODE, %d), ret = %d\n", fd, mode, rc);
		close(fd);
		return NULL;
	}

	/* 设置 SPI 频率 */
	rc = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &conf.speed);
	if (rc) {
		printf("err: ioctl(%d, SPI_IOC_WR_MAX_SPEED_HZ, %d), ret = %d\n", fd, speed, rc);
		close(fd);
		return NULL;
	}

	/* 创建 spi 对象 */
	ctxt = malloc(sizeof(spi_ctxt_t));
	ctxt->conf.mode = conf.mode;
	ctxt->conf.speed = conf.speed;
	ctxt->fd = fd;
	ctxt->inited = true;

	return (spi_t)ctxt;
}

int spi_write(spi_t spi, uint8_t* msg, uint32_t len)
{
	int rc = 0;
	int fd = 0;
	spi_ctxt_t* ctxt = NULL;

	/* 参数有效性校验 */
	if (NULL == spi) {
		printf("inval: spi\n");
		return -EINVAL;
	}

	if (NULL == msg) {
		printf("inval: msg\n");
		return -EINVAL;
	}

	ctxt = (spi_ctxt_t*)spi;
	if (true != ctxt->inited) {
		printf("err: spi not inited\n");
		return -ENOTINIT;
	}

	/* SPI 写数据 */
	fd = ctxt->fd;
	rc = write(fd, msg, len);
	if (rc != len) {
		printf("err: write(%d, msg, %d), ret = %d\n", fd, len, rc);
		return -EWRITE;
	}

	return 0;
}

void spi_deinit(spi_t spi)
{
	int fd = 0;
	spi_ctxt_t* ctxt = NULL;

	/* 参数有效性校验 */
	if (NULL == spi) {
		printf("inval: spi\n");
		return;
	}

	ctxt = (spi_ctxt_t*)spi;
	if (true != ctxt->inited) {
		printf("err: spi not inited\n");
		return;
	}

	/* 关闭 SPI 设备节点 */
	fd = ctxt->fd;
	close(fd);
	free(ctxt);
	ctxt = NULL;

	ctxt->inited = false;
}
